package tc.de.krawallmann.gravoblocks.game;

import java.util.HashSet;
import java.util.Set;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.Sound;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.state.transition.FadeInTransition;
import org.newdawn.slick.state.transition.FadeOutTransition;
import org.newdawn.slick.util.Log;

import tc.de.krawallmann.event.OKDialogListener;
import tc.de.krawallmann.extension.KeyboardInputMapExtension;
import tc.de.krawallmann.extension.SettingsExtension;
import tc.de.krawallmann.gravoblocks.resource.Language;
import tc.de.krawallmann.slick.game.AbstractExtendableGame;
import tc.de.krawallmann.slick.game.AbstractMMenuState;
import tc.de.krawallmann.twl.RootPane;
import tc.de.krawallmann.twl.widgets.MulticolorLabel;
import tc.de.krawallmann.twl.widgets.OKDialog;
import tc.de.krawallmann.twl.widgets.ZOrderPane;
import de.matthiasmann.twl.*;
import de.matthiasmann.twl.ScrollPane.Fixed;
import de.matthiasmann.twl.model.SimpleTableModel;

/**
 * The Class SettingsState lets the user customize some of the program's
 * settings.
 * @author KrawallMann
 * @version 1.0
 */
public class SettingsState extends AbstractMMenuState implements TableBase.Callback,
		OKDialogListener {

	/** The Constant ACTIONS. */
	private static final String[] ACTIONS =
			{
					Language.getString("SettingsState.0"), Language.getString("SettingsState.1"), Language.getString("SettingsState.6"), Language.getString("SettingsState.9"), Language.getString("SettingsState.12"), Language.getString("SettingsState.14"), Language.getString("SettingsState.16"), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
					Language.getString("SettingsState.19"), Language.getString("SettingsState.7") //$NON-NLS-1$ //$NON-NLS-2$
			};

	/** The blinking ghosts. */
	private transient ToggleButton blinkingGhosts;

	/** The blinking label. */
	private transient Label blinkingLabel;

	/** The click. */
	private transient Sound click;

	/** The defaults button. */
	private transient Button defaultsButton;

	/** The dialog. */
	private transient OKDialog dialog;

	/** The errordialog. */
	private transient OKDialog errordialog;

	/** The last column. */
	private transient int lastColumn = -1;

	/** The last row. */
	private transient int lastRow = -1;
	/** The ok button. */
	private transient Button okButton;

	private transient Widget overlay;

	/** The pane. */
	private transient ZOrderPane pane;

	/** The row active. */
	private transient boolean rowActive = false;

	/** The scroll pane. */
	private transient ScrollPane scrollPane;

	/** The "settings" label. */
	private transient Label settingsLabel;

	/** The table. */
	private transient Table table;

	/** The use sound. */
	private transient ToggleButton useSound;

	/** The use sound label. */
	private transient Label useSoundLabel;

	private transient ToggleButton specialTiles;

	private transient Label specialTilesLabel;

	/**
	 * Instantiates a new SettingsState.
	 * 
	 * @param menuID
	 *            the menu's id
	 * @param game
	 *            the game
	 */
	public SettingsState(final int menuID, final AbstractExtendableGame game) {
		super(menuID, game);
		if (getGame().getActions().size() != SettingsState.ACTIONS.length) {
			Log.warn("Invalid action sizes in settings"); //$NON-NLS-1$
		}
	}

	/*
	 * (non-Javadoc)
	 * @see de.matthiasmann.twl.TableBase.Callback#columnHeaderClicked(int)
	 */
	@Override
	public void columnHeaderClicked(final int arg0) {
		// Empty!
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractMMenuState#init(org.newdawn.
	 * slick.GameContainer, org.newdawn.slick.state.StateBasedGame)
	 */
	@Override
	public void init(final GameContainer container, final StateBasedGame game)
			throws SlickException {
		super.init(container, game);
		click = new Sound("tc/de/krawallmann/gravoblocks/resource/click.wav"); //$NON-NLS-1$
		setBackground(new Image("tc/de/krawallmann/gravoblocks/resource/mainmenu.jpg")); //$NON-NLS-1$
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractMMenuState#keyReleased(int,
	 * char)
	 */
	@Override
	public void keyReleased(final int key, final char keyCode) {
		super.keyReleased(key, keyCode);
		if (key == Input.KEY_ESCAPE) {
			dialog.setVisible(false);
			errordialog.setVisible(false);
			overlay.setVisible(false);
			rowActive = false;
			if ((lastRow != -1) && (lastColumn != -1)) {
				((SimpleTableModel) table.getModel()).setCell(lastRow, 1, Input
						.getKeyName(getGame().getExtension(KeyboardInputMapExtension.class)
								.getKeyFor(getGame().getActions().get(lastRow))));
			}
		} else if (rowActive) {
			dialog.setVisible(false);
			if (validateKeys(key)) {
				overlay.setVisible(false);
				getGame().getExtension(KeyboardInputMapExtension.class).remove(
						getGame().getExtension(KeyboardInputMapExtension.class).getKeyFor(
								getGame().getActions().get(lastRow)),
						getGame().getActions().get(lastRow));
				getGame().getExtension(KeyboardInputMapExtension.class).put(key,
						getGame().getActions().get(lastRow));
				rowActive = false;
				((SimpleTableModel) table.getModel()).setCell(lastRow, 1, Input
						.getKeyName(getGame().getExtension(KeyboardInputMapExtension.class)
								.getKeyFor(getGame().getActions().get(lastRow))));
			} else {
				dialog.setVisible(false);
				rowActive = false;
				if ((lastRow != -1) && (lastColumn != -1)) {
					((SimpleTableModel) table.getModel()).setCell(lastRow, 1, Input
							.getKeyName(getGame().getExtension(KeyboardInputMapExtension.class)
									.getKeyFor(getGame().getActions().get(lastRow))));
				}
				overlay.setVisible(true);
				errordialog.setVisible(true);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.twlslick.BasicTWLGameState#layoutRootPane()
	 */
	@Override
	public void layoutRootPane() {
		super.layoutRootPane();

		// Adjust the sizes
		pane.setSize(getContainer().getWidth(), getContainer().getHeight());
		table.adjustSize();
		table.setSize(700, Math.max(table.getHeight(), 350));
		scrollPane.setSize(730, 250);
		dialog.adjustSize();
		dialog.layout();
		errordialog.adjustSize();
		errordialog.layout();
		blinkingLabel.adjustSize();
		blinkingGhosts.setSize(25, 25);
		useSoundLabel.adjustSize();
		useSound.setSize(25, 25);
		settingsLabel.adjustSize();
		okButton.adjustSize();
		defaultsButton.adjustSize();
		overlay.setSize(getContainer().getWidth(), getContainer().getHeight());
		specialTiles.adjustSize();
		specialTilesLabel.adjustSize();

		// Set the positions
		scrollPane.setPosition(50, 220);
		dialog.setPosition((getContainer().getWidth() - dialog.getWidth()) / 2, (getContainer()
				.getHeight() - dialog.getHeight()) / 2);
		errordialog.setPosition((getContainer().getWidth() - errordialog.getWidth()) / 2,
				(getContainer().getHeight() - errordialog.getHeight()) / 2);
		okButton.setPosition(750 - okButton.getWidth(), 500);
		defaultsButton.setPosition(okButton.getX() - defaultsButton.getWidth() - 20, 500);
		blinkingGhosts.setPosition(50, 160);
		blinkingLabel.setPosition(90, 150);
		useSound.setPosition(350, 160);
		useSoundLabel.setPosition(390, 150);
		
		specialTiles.setPosition(520, 160);
		specialTilesLabel.setPosition(560, 150);
		
		overlay.setPosition(0, 0);
		settingsLabel.setPosition((getContainer().getWidth() - settingsLabel.getWidth()) / 2, 20);
	}

	/*
	 * (non-Javadoc)
	 * @see de.matthiasmann.twl.TableBase.Callback#mouseDoubleClicked(int, int)
	 */
	@Override
	public void mouseDoubleClicked(final int row, final int column) {
		if ((lastRow != -1) && (lastColumn != -1)) {
			((SimpleTableModel) table.getModel()).setCell(lastRow, 1, Input.getKeyName(getGame()
					.getExtension(KeyboardInputMapExtension.class).getKeyFor(
							getGame().getActions().get(lastRow))));
		}
		dialog.setVisible(true);
		overlay.setVisible(true);
		lastRow = row;
		lastColumn = column;
		rowActive = true;
		if (column != 0) {
			((SimpleTableModel) table.getModel()).setCell(row, 1,
					Language.getString("SettingsState.33")); //$NON-NLS-1$
		}
	}

	/*
	 * (non-Javadoc)
	 * @see de.matthiasmann.twl.TableBase.Callback#mouseRightClick(int, int,
	 * de.matthiasmann.twl.Event)
	 */
	@Override
	public void mouseRightClick(final int arg0, final int arg1, final Event arg2) {
		// Empty!
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.twl.OKDialogListener#okPressed()
	 */
	@Override
	public void okPressed() {
		dialog.setVisible(false);
		overlay.setVisible(false);
		rowActive = false;
		if ((lastRow != -1) && (lastColumn != -1)) {
			((SimpleTableModel) table.getModel()).setCell(lastRow, 1, Input.getKeyName(getGame()
					.getExtension(KeyboardInputMapExtension.class).getKeyFor(
							getGame().getActions().get(lastRow))));
		}
	}

	/**
	 * Validate keys.
	 * 
	 * @param newOne
	 *            the new one
	 * @return true, if successful
	 */
	public boolean validateKeys(final int newOne) {
		final Set<Integer> set = new HashSet<Integer>();
		set.add(newOne);
		boolean ret = true;
		for (final String s : getGame().getActions()) {
			if (!set.add(getGame().getExtension(KeyboardInputMapExtension.class).getKeyFor(s))) {
				ret = false;
				break;
			}
		}
		return ret;
	}

	/**
	 * Creates the key settings.
	 */
	protected void createKeySettings() {
		// Create the widgets
		table = new Table(new SimpleTableModel(new String[] {
				Language.getString("SettingsState.26"), Language.getString("SettingsState.27") //$NON-NLS-1$ //$NON-NLS-2$
				}));
		table.setTooltipContent(Language.getString("SettingsState.28")); //$NON-NLS-1$
		table.setTheme("/table"); //$NON-NLS-1$

		for (int i = 0; i < getGame().getActions().size(); i++) {
			((SimpleTableModel) table.getModel()).addRow(SettingsState.ACTIONS[i], Input
					.getKeyName(getGame().getExtension(KeyboardInputMapExtension.class).getKeyFor(
							getGame().getActions().get(i))));
		}
		scrollPane = new ScrollPane(table);
		scrollPane.setFixed(Fixed.HORIZONTAL);

		dialog =
				new OKDialog(
						Language.getString("SettingsState.29"), Language.getString("SettingsState.30"), this); //$NON-NLS-1$ //$NON-NLS-2$
		dialog.setColor(MulticolorLabel.Color.BLUE);
		dialog.setVisible(false);

		errordialog =
				new OKDialog(
						Language.getString("SettingsState.31"), Language.getString("SettingsState.32"), this); //$NON-NLS-1$ //$NON-NLS-2$
		errordialog.setVisible(false);

		dialog.setTheme("yellowdialog"); //$NON-NLS-1$
		errordialog.setTheme("reddialog"); //$NON-NLS-1$

		// Add callbacks
		table.addCallback(this);

		// Add the widgets
		pane.add(dialog);
		pane.add(scrollPane);
		pane.add(errordialog);
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.twlslick.BasicTWLGameState#createRootPane()
	 */
	@Override
	protected RootPane createRootPane() {
		final RootPane rootpane = super.createRootPane();

		// Set root theme
		rootpane.setTheme(""); //$NON-NLS-1$

		// Create the widgets
		createWidgets();

		// Add callbacks
		addCallbacks();

		// Add the widgets
		addWidgets(rootpane);

		// Make the widgets focusable
		makeFocusable();

		return rootpane;
	}

	protected void makeFocusable() {
		makeFocusable(defaultsButton);
		makeFocusable(okButton);
		makeFocusable(blinkingGhosts);
		makeFocusable(useSound);
		makeFocusable(specialTiles);
	}

	protected void addWidgets(final RootPane rootpane) {
		pane.add(settingsLabel);
		pane.add(okButton);
		pane.add(defaultsButton);
		pane.add(blinkingGhosts);
		pane.add(blinkingLabel);
		pane.add(specialTiles);
		pane.add(specialTilesLabel);
		pane.add(useSound);
		pane.add(useSoundLabel);
		pane.add(overlay);
		pane.moveToFront(overlay);
		pane.moveToFront(dialog);
		pane.moveToFront(errordialog);
		rootpane.add(pane);
	}

	protected void addCallbacks() {
		okButton.addCallback(new Runnable() {
			@Override
			public void run() {
				SettingsExtension.setSetting(
						GravoBlocksGame.USE_SOUND, useSound.isActive());
				SettingsExtension.setSetting(
						GravoBlocksGame.BLINKING_GHOST, blinkingGhosts.isActive());
				SettingsExtension.setSetting(
						GravoBlocksGame.SPECIAL_TILES, specialTiles.isActive());
				getGame().getExtension(SettingsExtension.class).store();
				getGame().enterState(GravoBlocksGame.MAINMENUSTATE, new FadeOutTransition(),
						new FadeInTransition());
				try {
					getGame().getState(GravoBlocksGame.GAMEPLAYSTATE).leave(getContainer(),
							getGame());// Circumvents a bug!
				} catch (final SlickException exception) {
					Log.warn(exception.getMessage(), exception);
				}
				getGame().getExtension(SettingsExtension.class);
				if (SettingsExtension.getSetting(
						GravoBlocksGame.USE_SOUND)) {
					click.play();
				}
			}
		});

		defaultsButton.addCallback(new Runnable() {
			@Override
			public void run() {
				getGame().getExtension(SettingsExtension.class).setDefaults();
				getGame().getExtension(KeyboardInputMapExtension.class).setDefaults();
				blinkingGhosts.setActive(SettingsExtension
						.getSetting(GravoBlocksGame.BLINKING_GHOST));
				useSound.setActive(SettingsExtension.getSetting(
						GravoBlocksGame.USE_SOUND));
				specialTiles.setActive(SettingsExtension.getSetting(
						GravoBlocksGame.SPECIAL_TILES));
				for (int i = 0; i < getGame().getActions().size(); i++) {
					((SimpleTableModel) table.getModel()).setCell(i, 1, Input.getKeyName(getGame()
							.getExtension(KeyboardInputMapExtension.class).getKeyFor(
									getGame().getActions().get(i))));
				}
			}
		});
	}

	protected void createWidgets() {
		pane = new ZOrderPane();
		pane.setTheme(""); //$NON-NLS-1$
		createKeySettings();
		settingsLabel = new Label(Language.getString("SettingsState.3")); //$NON-NLS-1$
		settingsLabel.setTheme("label60"); //$NON-NLS-1$

		okButton = new Button(Language.getString("SettingsState.8")); //$NON-NLS-1$
		okButton.setTheme("button40"); //$NON-NLS-1$
		okButton.setTooltipContent(Language.getString("SettingsState.13")); //$NON-NLS-1$

		defaultsButton = new Button(Language.getString("SettingsState.11")); //$NON-NLS-1$
		defaultsButton.setTheme("button40"); //$NON-NLS-1$
		defaultsButton.setTooltipContent(Language.getString("SettingsState.13")); //$NON-NLS-1$

		blinkingGhosts = new ToggleButton();
		blinkingGhosts.setTooltipContent(Language.getString("SettingsState.15")); //$NON-NLS-1$
		blinkingGhosts.setTheme("checkbox"); //$NON-NLS-1$

		blinkingLabel = new Label(Language.getString("SettingsState.17")); //$NON-NLS-1$
		blinkingLabel.setLabelFor(blinkingGhosts);
		blinkingLabel.setTheme("label30"); //$NON-NLS-1$

		useSound = new ToggleButton();
		useSound.setTooltipContent(Language.getString("SettingsState.21")); //$NON-NLS-1$
		useSound.setTheme("checkbox"); //$NON-NLS-1$
		
		specialTiles = new ToggleButton();
		specialTiles.setTooltipContent(Language.getString("SettingsState.23"));  //$NON-NLS-1$
		specialTiles.setTheme("checkbox"); //$NON-NLS-1$
		
		specialTilesLabel = new Label(Language.getString("SettingsState.24")); //$NON-NLS-1$
		specialTilesLabel.setLabelFor(specialTiles);
		specialTilesLabel.setTheme("label30"); //$NON-NLS-1$

		useSoundLabel = new Label(Language.getString("SettingsState.22")); //$NON-NLS-1$
		useSoundLabel.setLabelFor(useSound);
		useSoundLabel.setTheme("label30"); //$NON-NLS-1$

		blinkingGhosts.setActive(SettingsExtension.getSetting(
				GravoBlocksGame.BLINKING_GHOST));
		useSound.setActive(SettingsExtension.getSetting(
				GravoBlocksGame.USE_SOUND));
		specialTiles.setActive(SettingsExtension.getSetting(
				GravoBlocksGame.SPECIAL_TILES));
		overlay = new Widget();
		overlay.setTheme("overlay"); //$NON-NLS-1$
		overlay.setVisible(false);
	}

	@Override
	public void cancelPressed() {
		//No YesNoDialog used!
	}

}
